package upbeat.types;

import java.util.ArrayList;

import upbeat.jmusiclib.JMusicWrapper;


/**
 * This class stands for a piece of playable music. It could related to a single
 * MIDI file, or it could be a combination of different phrases.
 * 
 * @author Fan Lin
 * 
 */



public class MusicPhrase {
	// Static Fields.
	public static MusicPhrase getREST() {
		return new MusicPhrase("REST", 1);
	}

	// Basic Info
	String midiFileName = null;
	int length = 0;
	int repeats = 1;
	ArrayList<MusicPhrase> children = null;
	
	/**
	 * Create an empty Music Phrase.
	 */
	public MusicPhrase()
	{
		children = new ArrayList<MusicPhrase>();
	}
	
	/**
	 * Create a music phrase from a MIDI file.
	 * 
	 * @param midiFileName
	 */
	public MusicPhrase(String midiFileName)
	{
		this(midiFileName, (new JMusicWrapper()).length(midiFileName));
	}
	
	/**
	 * Create a music phrase from a MIDI file.
	 * 
	 * @param midiFileName
	 */
	public MusicPhrase(String midiFileName, int length)
	{
		this.midiFileName = midiFileName;
		this.length = length;
	}
	
	/** 
	 * Set the number of repeat of current phrase.
	 * 
	 * @param n number of repeats, -1 for repeat forever
	 */
	public void setRepeat(int n)
	{
		repeats = n; 
	}
	
	/**
	 * Get the the length of the phrase, includes all children phrases and repeates.
	 * 
	 * @return phrase length in number of measures, -1 for infinite.
	 */
	public int getLength() {
		if (repeats >= 0) {
			return this.length * repeats;
		} else {
			return -1;
		}
	}
	
	public void appendPhrase(MusicPhrase p) throws Exception {
		if (isLeaf()) { 
			throw new Exception("Cannot append to a leaf phrase");
		}
		
		children.add(p);
		if (p.getLength() == -1) {
			this.length = -1;
		} else {
			this.length += p.getLength();
		}		
	}
	
	public boolean isLeaf() {
		return (midiFileName != null);
	}
	
	public boolean isInfinte() {
		return (repeats == -1);
	}
	
	// Playing control variables.
	int repeatsLeft;
	int currentChildPlaying; 
	int currentMeasurePlaying;
	
	// Playing control methods.
	public void reset() {
		repeatsLeft = repeats;
		if (!isLeaf()) {
			for (MusicPhrase p : children) {
				p.reset();
			}
			currentChildPlaying = 0;
		} else {
			currentMeasurePlaying = 0;
		}
	}
	
	public Measure playNext() {
		if (repeatsLeft == 0 && !isInfinte()) {
			return null;
		}
		
		if (isLeaf()) {
			while (repeatsLeft > 0 || isInfinte()) {
				if (currentMeasurePlaying < length) {
					return new Measure(midiFileName, currentMeasurePlaying++);
				} else {
					repeatsLeft--;
					currentMeasurePlaying= 0;
				}
			}
			return null;
		} else {
			while (repeatsLeft > 0 || isInfinte()) {
				while (currentChildPlaying < children.size()) {
					Measure ret = children.get(currentChildPlaying).playNext();
					if (ret != null) {
						return ret;
					}
					currentChildPlaying++;
				}
				
				repeatsLeft--;
				for (MusicPhrase p : children) {
					p.reset();
				}
				currentChildPlaying = 0;
			}
			return null;
		}
		
	}
}
